package org.xx.armory.spring5.security;

import org.springframework.http.HttpMethod;

import java.util.Arrays;
import java.util.Objects;

import static java.util.Arrays.stream;
import static org.apache.commons.lang3.ArrayUtils.contains;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.xx.armory.commons.Validators.rejectIfBlank;
import static org.xx.armory.commons.Validators.rejectIfNull;

/**
 * 表示访问指定资源的权限。
 */
public final class AuthorityEntry {
    /**
     * 表示任意权限
     */
    public static final String PUBLIC_AUTHORITY = "*";

    /**
     * 表示任意已登录的权限。
     */
    public static final String ANY_AUTHENTICATED_AUTHORITY = "?";

    private final HttpMethod method;
    private final String path;
    private final String[] authorities;

    /**
     * 初始化资源权限。
     *
     * @param path
     *         资源的路径。
     * @param authorities
     *         访问资源需要的权限，多个权限表示任一一个权限均可访问。
     */
    public AuthorityEntry(
            String path,
            String... authorities
    ) {
        this(null, path, authorities);
    }

    /**
     * 初始化资源权限。
     *
     * @param path
     *         资源的路径。
     * @param authorities
     *         访问资源需要的权限，多个权限表示任一一个权限均可访问。
     */
    public AuthorityEntry(
            String path,
            int... authorities
    ) {
        this(null, path, authorities);
    }

    /**
     * 初始化资源权限。
     *
     * @param method
     *         访问资源的方法。
     * @param path
     *         资源的路径。
     * @param authorities
     *         访问资源需要的权限，多个权限表示任一一个权限均可访问。
     */
    public AuthorityEntry(
            HttpMethod method,
            String path,
            String... authorities
    ) {
        this.method = method;
        this.path = rejectIfBlank(path, "path").trim();
        rejectIfNull(authorities, "authorities");
        final var authorities_ = stream(authorities).filter(s -> !isBlank(s)).map(String::trim).toArray(String[]::new);
        if (contains(authorities_, PUBLIC_AUTHORITY)) {
            this.authorities = new String[]{PUBLIC_AUTHORITY};
        } else if (contains(authorities, ANY_AUTHENTICATED_AUTHORITY)) {
            this.authorities = new String[]{ANY_AUTHENTICATED_AUTHORITY};
        } else {
            this.authorities = authorities_;
        }
    }

    /**
     * 初始化资源权限。
     *
     * @param method
     *         访问资源的方法。
     * @param path
     *         资源的路径。
     * @param authorities
     *         访问资源需要的权限，多个权限表示任一一个权限均可访问。
     */
    public AuthorityEntry(
            HttpMethod method,
            String path,
            int... authorities
    ) {
        this.method = method;
        this.path = rejectIfBlank(path, "path").trim();
        rejectIfNull(authorities, "authorities");
        this.authorities = stream(authorities).mapToObj(String::valueOf).toArray(String[]::new);
    }

    /**
     * 获取访问资源的方法。
     *
     * @return 访问资源的方法。
     */
    public final HttpMethod getMethod() {
        return this.method;
    }

    /**
     * 获取资源的路径。
     *
     * @return 资源的路径。
     */
    public final String getPath() {
        return this.path;
    }

    /**
     * 判断是否无需任何权限即可访问。
     *
     * @return 是否无需任何权限即可访问。
     */
    public final boolean isPublic() {
        return this.authorities.length == 1 && PUBLIC_AUTHORITY.equals(this.authorities[0]);
    }

    /**
     * 判断是否任何已登录的权限即可访问。
     *
     * @return 是否任何已登录的权限即可访问。
     */
    public final boolean isAnyAuthenticated() {
        return this.authorities.length > 0 && ANY_AUTHENTICATED_AUTHORITY.equals(this.authorities[0]);
    }

    /**
     * 判断是否只有单一权限可以访问此资源。
     *
     * @return 是否只有单一权限可以访问此资源。
     */
    public final boolean isSingle() {
        return this.authorities.length == 1 && !PUBLIC_AUTHORITY.equals(this.authorities[0]) && !ANY_AUTHENTICATED_AUTHORITY.equals(this.authorities[0]);
    }

    /**
     * 判断是否多个权限都可以访问此资源。
     *
     * @return 是否多个权限都可以访问此资源。
     */
    public final boolean isMultiple() {
        return this.authorities.length > 1;
    }

    /**
     * 判断是否无权限可以访问此资源。
     *
     * @return 是否无权限可以访问此资源。
     */
    public final boolean isEmpty() {
        return this.authorities.length == 0;
    }

    public final String getAuthority() {
        return this.authorities[0];
    }

    /**
     * 获取访问资源需要的权限。
     *
     * @return 访问资源需要的权限。
     */
    public final String[] getAuthorities() {
        return this.authorities;
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.method, this.path, this.authorities);
    }

    @Override
    public boolean equals(
            Object obj
    ) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof AuthorityEntry)) {
            return false;
        }
        final var other = (AuthorityEntry) obj;
        return this.method == other.method
                && Objects.equals(this.path, other.path)
                && Arrays.equals(this.authorities, other.authorities);
    }

    @Override
    public String toString() {
        return "AuthorityEntry("
                + "method=" + this.method + ","
                + "path=" + this.path + ","
                + "authorities=" + Arrays.toString(this.authorities)
                + ")";
    }
}
